<!doctype html>
<html lang="zh-CN"><head><script src="/livereload.js?mindelay=10&amp;v=2&amp;port=1313&amp;path=livereload" data-no-instant defer></script>
    <title>薛尧的博客</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="" />

    
    
    
    <link rel="stylesheet" href="../../../css/theme.min.css">

    
    
    
    
    <link rel="stylesheet" href="../../../css/custom.min.css">
    

    
</head>
<body>
        <div id="content" class="mx-auto"><header class="container mt-sm-5 mt-4 mb-4 mt-xs-1">
    <div class="row">
        
        <div class="col-sm-4 col-12 text-sm-right text-center pt-sm-4">
            <a href="../../../" class="text-decoration-none">
                <img id="home-image" class="rounded-circle"
                    
                        src="https://ueyao.github.io/image-hosting/blog/avatar.png"
                    
                />
            </a>
        </div>
        <div class="col-sm-8 col-12 text-sm-left text-center">
        
            <h2 class="m-0 mb-2 mt-4">
                <a href="../../../" class="text-decoration-none">
                    
                        薛尧
                    
                </a>
            </h2>
            <p class="text-muted mb-1">
                
                    Java Developer | Short Video Creator
                
            </p>
            <ul id="nav-links" class="list-inline mb-2">
                
                
                    <li class="list-inline-item">
                        <a class="badge badge-white " href="../../../about/" title="关于">关于</a>
                    </li>
                
                    <li class="list-inline-item">
                        <a class="badge badge-white " href="../../../post/" title="文章">文章</a>
                    </li>
                
                    <li class="list-inline-item">
                        <a class="badge badge-white " href="../../../categories/" title="分类">分类</a>
                    </li>
                
            </ul>
            <ul id="nav-social" class="list-inline">
                
                    <li class="list-inline-item mr-3">
                        <a href="http://github.com/flowstone" target="_blank">
                            <i class="fab fa-github fa-1x text-muted"></i>
                        </a>
                    </li>
                
                    <li class="list-inline-item mr-3">
                        <a href="mailto:xueyao.me#gmail.com" target="_blank">
                            <i class="fas fa-at fa-1x text-muted"></i>
                        </a>
                    </li>
                
            </ul>
        </div>
    </div>
    <hr />
</header>
<div class="container">
    <div class="pl-sm-2">
        <div class="mb-3">
            <h3 class="mb-0">Java多线程-线程通信</h3>
            
            <small class="text-muted">发布于 2019-09-03</small>
        </div>

        <article>
            <h2 id="通信的方式">通信的方式</h2>
<p>要想实现多个线程之间的协同，如：线程执行先后顺序、获取某个线程执行的结果等等。涉及到线程之间的相互通信，分为下面四类：</p>
<ul>
<li>文件共享</li>
<li>网络共享</li>
<li>共享变量</li>
<li>JDK提供的线程协调API
<ul>
<li>suspend/resume、wait/notify、park/unpark</li>
</ul>
</li>
</ul>
<h3 id="文件共享">文件共享</h3>
<p><img src="https://ueyao.github.io/image-hosting/blog/2019/9/thread-communication-01.png" alt=""></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MainTest</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 线程1 - 写入数据</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">while</span> (<span style="color:#66d9ef">true</span>) {
</span></span><span style="display:flex;"><span>          Files.<span style="color:#a6e22e">write</span>(Paths.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;test.log&#34;</span>),
</span></span><span style="display:flex;"><span>          content <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;当前时间&#34;</span> <span style="color:#f92672">+</span> String.<span style="color:#a6e22e">valueOf</span>(System.<span style="color:#a6e22e">currentTimeMillis</span>()));
</span></span><span style="display:flex;"><span>          Thread.<span style="color:#a6e22e">sleep</span>(1000L);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      } <span style="color:#66d9ef">catch</span> (Exception e) {
</span></span><span style="display:flex;"><span>        e.<span style="color:#a6e22e">printStackTrace</span>();
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }).<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 线程2 - 读取数据</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">while</span> (<span style="color:#66d9ef">true</span>) {
</span></span><span style="display:flex;"><span>          Thread.<span style="color:#a6e22e">sleep</span>(1000L);
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">byte</span><span style="color:#f92672">[]</span> allBytes <span style="color:#f92672">=</span> Files.<span style="color:#a6e22e">readAllBytes</span>(Paths.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;test.log&#34;</span>));
</span></span><span style="display:flex;"><span>          System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#66d9ef">new</span> String(allBytes));
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      } <span style="color:#66d9ef">catch</span> (Exception e) {
</span></span><span style="display:flex;"><span>        e.<span style="color:#a6e22e">printStackTrace</span>();
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }).<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="变量共享">变量共享</h3>
<p><img src="https://ueyao.github.io/image-hosting/blog/2019/9/thread-communication-02.png" alt=""></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MainTest</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// 共享变量</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> String content <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;空&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 线程1 - 写入数据</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">while</span> (<span style="color:#66d9ef">true</span>) {
</span></span><span style="display:flex;"><span>          content <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;当前时间&#34;</span> <span style="color:#f92672">+</span> String.<span style="color:#a6e22e">valueOf</span>(System.<span style="color:#a6e22e">currentTimeMillis</span>());
</span></span><span style="display:flex;"><span>          Thread.<span style="color:#a6e22e">sleep</span>(1000L);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      } <span style="color:#66d9ef">catch</span> (Exception e) {
</span></span><span style="display:flex;"><span>        e.<span style="color:#a6e22e">printStackTrace</span>();
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }).<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 线程2 - 读取数据</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">while</span> (<span style="color:#66d9ef">true</span>) {
</span></span><span style="display:flex;"><span>          Thread.<span style="color:#a6e22e">sleep</span>(1000L);
</span></span><span style="display:flex;"><span>          System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(content);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      } <span style="color:#66d9ef">catch</span> (Exception e) {
</span></span><span style="display:flex;"><span>        e.<span style="color:#a6e22e">printStackTrace</span>();
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }).<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="网络共享">网络共享</h3>
<h3 id="线程协作jdk-api">线程协作－JDK API</h3>
<p>JDK中对于需要多线程协作完成某一任务的场景，提供了对应API支持。</p>
<p>多线程协作的典型场景是：生产者－消费者模型。(线程阻塞、线程唤醒)</p>
<p>示例：线程１去买包子，没有包子，则不再执行。线程２生产出包子，通知线程－１继续执行。</p>
<p><img src="https://ueyao.github.io/image-hosting/blog/2019/9/thread-communication-03.png" alt=""></p>
<h4 id="api-被弃用的suspend和resume">API-被弃用的suspend和resume</h4>
<p>作用：调用suspend挂起目标线程，通过resume可以恢复线程执行。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#75715e">/** 包子店 */</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> Object baozidian <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">/** 正常的suspend/resume */</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">suspendResumeTest</span>() <span style="color:#66d9ef">throws</span> Exception {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 启动线程</span>
</span></span><span style="display:flex;"><span>    Thread consumerThread <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (baozidian <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span>) { <span style="color:#75715e">// 如果没包子，则进入等待</span>
</span></span><span style="display:flex;"><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;1、进入等待&#34;</span>);
</span></span><span style="display:flex;"><span>            Thread.<span style="color:#a6e22e">currentThread</span>().<span style="color:#a6e22e">suspend</span>();
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;2、买到包子，回家&#34;</span>);
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>    consumerThread.<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 3秒之后，生产一个包子</span>
</span></span><span style="display:flex;"><span>    Thread.<span style="color:#a6e22e">sleep</span>(3000L);
</span></span><span style="display:flex;"><span>    baozidian <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Object();
</span></span><span style="display:flex;"><span>    consumerThread.<span style="color:#a6e22e">resume</span>();
</span></span><span style="display:flex;"><span>    System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;3、通知消费者&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>被弃用的主要原因是，容易写出不死锁的代码。所以用wait/notify和park/unpark机制对它进行替代</p>
<h4 id="suspend和resume死锁示例">suspend和resume死锁示例</h4>
<p>1、同步代码中使用</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>	<span style="color:#75715e">/** 死锁的suspend/resume。 suspend并不会像wait一样释放锁，故此容易写出死锁代码 */</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">suspendResumeDeadLockTest</span>() <span style="color:#66d9ef">throws</span> Exception {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 启动线程</span>
</span></span><span style="display:flex;"><span>		Thread consumerThread <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> (baozidian <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span>) { <span style="color:#75715e">// 如果没包子，则进入等待</span>
</span></span><span style="display:flex;"><span>				System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;1、进入等待&#34;</span>);
</span></span><span style="display:flex;"><span>				<span style="color:#75715e">// 当前线程拿到锁，然后挂起</span>
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">synchronized</span> (<span style="color:#66d9ef">this</span>) {
</span></span><span style="display:flex;"><span>					Thread.<span style="color:#a6e22e">currentThread</span>().<span style="color:#a6e22e">suspend</span>();
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;2、买到包子，回家&#34;</span>);
</span></span><span style="display:flex;"><span>		});
</span></span><span style="display:flex;"><span>		consumerThread.<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 3秒之后，生产一个包子</span>
</span></span><span style="display:flex;"><span>		Thread.<span style="color:#a6e22e">sleep</span>(3000L);
</span></span><span style="display:flex;"><span>		baozidian <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Object();
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 争取到锁以后，再恢复consumerThread</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">synchronized</span> (<span style="color:#66d9ef">this</span>) {
</span></span><span style="display:flex;"><span>			consumerThread.<span style="color:#a6e22e">resume</span>();
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;3、通知消费者&#34;</span>);
</span></span><span style="display:flex;"><span>	}
</span></span></code></pre></div><p>2、suspend比resume后执行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#75715e">/** 导致程序永久挂起的suspend/resume */</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">suspendResumeDeadLockTest2</span>() <span style="color:#66d9ef">throws</span> Exception {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 启动线程</span>
</span></span><span style="display:flex;"><span>		Thread consumerThread <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> (baozidian <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span>) {
</span></span><span style="display:flex;"><span>				System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;1、没包子，进入等待&#34;</span>);
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">try</span> { <span style="color:#75715e">// 为这个线程加上一点延时</span>
</span></span><span style="display:flex;"><span>					Thread.<span style="color:#a6e22e">sleep</span>(5000L);
</span></span><span style="display:flex;"><span>				} <span style="color:#66d9ef">catch</span> (InterruptedException e) {
</span></span><span style="display:flex;"><span>					e.<span style="color:#a6e22e">printStackTrace</span>();
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>				<span style="color:#75715e">// 这里的挂起执行在resume后面</span>
</span></span><span style="display:flex;"><span>				Thread.<span style="color:#a6e22e">currentThread</span>().<span style="color:#a6e22e">suspend</span>();
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;2、买到包子，回家&#34;</span>);
</span></span><span style="display:flex;"><span>		});
</span></span><span style="display:flex;"><span>		consumerThread.<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 3秒之后，生产一个包子</span>
</span></span><span style="display:flex;"><span>		Thread.<span style="color:#a6e22e">sleep</span>(3000L);
</span></span><span style="display:flex;"><span>		baozidian <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Object();
</span></span><span style="display:flex;"><span>		consumerThread.<span style="color:#a6e22e">resume</span>();
</span></span><span style="display:flex;"><span>		System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;3、通知消费者&#34;</span>);
</span></span><span style="display:flex;"><span>		consumerThread.<span style="color:#a6e22e">join</span>();
</span></span><span style="display:flex;"><span>	}
</span></span></code></pre></div><h4 id="ｗaitnotify机制">ｗait/notify机制</h4>
<p>这些方法只能由同一对象锁的持有者线程调用，也就是写在同步块里面，否则会抛出IllegalMonitorStateException异常。
wait方法导致当前线程等待，加入该对象的等待集合中，并且放弃当前持有的对象锁。
notify/notifyAll方法唤醒一个或所有正在等待这个对象锁的线程。
注意：虽然会wait自动解锁，但是对顺序有要求，如果在notify被调用之后，才开始wait方法的调用，线程会永远处于WAITING状态。</p>
<h4 id="waitnotify代码示例">wait/notify代码示例</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#75715e">/** 正常的wait/notify */</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">waitNotifyTest</span>() <span style="color:#66d9ef">throws</span> Exception {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 启动线程</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">synchronized</span> (<span style="color:#66d9ef">this</span>) {
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">while</span> (baozidian <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span>) { <span style="color:#75715e">// 如果没包子，则进入等待</span>
</span></span><span style="display:flex;"><span>					<span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>						System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;1、进入等待&#34;</span>);
</span></span><span style="display:flex;"><span>						<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">wait</span>();
</span></span><span style="display:flex;"><span>					} <span style="color:#66d9ef">catch</span> (InterruptedException e) {
</span></span><span style="display:flex;"><span>						e.<span style="color:#a6e22e">printStackTrace</span>();
</span></span><span style="display:flex;"><span>					}
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;2、买到包子，回家&#34;</span>);
</span></span><span style="display:flex;"><span>		}).<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 3秒之后，生产一个包子</span>
</span></span><span style="display:flex;"><span>		Thread.<span style="color:#a6e22e">sleep</span>(3000L);
</span></span><span style="display:flex;"><span>		baozidian <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Object();
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">synchronized</span> (<span style="color:#66d9ef">this</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">notifyAll</span>();
</span></span><span style="display:flex;"><span>			System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;3、通知消费者&#34;</span>);
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span></code></pre></div><p>造成死锁的示例</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#75715e">/** 会导致程序永久等待的wait/notify */</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">waitNotifyDeadLockTest</span>() <span style="color:#66d9ef">throws</span> Exception {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 启动线程</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> (baozidian <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span>) { <span style="color:#75715e">// 如果没包子，则进入等待</span>
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>					Thread.<span style="color:#a6e22e">sleep</span>(5000L);
</span></span><span style="display:flex;"><span>				} <span style="color:#66d9ef">catch</span> (InterruptedException e1) {
</span></span><span style="display:flex;"><span>					e1.<span style="color:#a6e22e">printStackTrace</span>();
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">synchronized</span> (<span style="color:#66d9ef">this</span>) {
</span></span><span style="display:flex;"><span>					<span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>						System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;1、进入等待&#34;</span>);
</span></span><span style="display:flex;"><span>						<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">wait</span>();
</span></span><span style="display:flex;"><span>					} <span style="color:#66d9ef">catch</span> (InterruptedException e) {
</span></span><span style="display:flex;"><span>						e.<span style="color:#a6e22e">printStackTrace</span>();
</span></span><span style="display:flex;"><span>					}
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;2、买到包子，回家&#34;</span>);
</span></span><span style="display:flex;"><span>		}).<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 3秒之后，生产一个包子</span>
</span></span><span style="display:flex;"><span>		Thread.<span style="color:#a6e22e">sleep</span>(3000L);
</span></span><span style="display:flex;"><span>		baozidian <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Object();
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">synchronized</span> (<span style="color:#66d9ef">this</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">notifyAll</span>();
</span></span><span style="display:flex;"><span>			System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;3、通知消费者&#34;</span>);
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span></code></pre></div><h4 id="parkunpark机制">park/unpark机制</h4>
<p>线程调用park则等待“许可”，unpark方法为指定线程提供“许可(permit)”
不要求park和unpark方法的调用顺序。
多次调用unpark之后，再调用park，线程会直接运行。
但不会叠加，也就是说，连续多次调用park方法，第一次会拿到&quot;许可&quot;直接运行,后续调用会进入等待。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#75715e">/** 正常的park/unpark */</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">parkUnparkTest</span>() <span style="color:#66d9ef">throws</span> Exception {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 启动线程</span>
</span></span><span style="display:flex;"><span>		Thread consumerThread <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">while</span> (baozidian <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span>) { <span style="color:#75715e">// 如果没包子，则进入等待</span>
</span></span><span style="display:flex;"><span>				System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;1、进入等待&#34;</span>);
</span></span><span style="display:flex;"><span>				LockSupport.<span style="color:#a6e22e">park</span>();
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;2、买到包子，回家&#34;</span>);
</span></span><span style="display:flex;"><span>		});
</span></span><span style="display:flex;"><span>		consumerThread.<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 3秒之后，生产一个包子</span>
</span></span><span style="display:flex;"><span>		Thread.<span style="color:#a6e22e">sleep</span>(3000L);
</span></span><span style="display:flex;"><span>		baozidian <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Object();
</span></span><span style="display:flex;"><span>		LockSupport.<span style="color:#a6e22e">unpark</span>(consumerThread);
</span></span><span style="display:flex;"><span>		System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;3、通知消费者&#34;</span>);
</span></span><span style="display:flex;"><span>	}
</span></span></code></pre></div><p>造成死锁的示例</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#75715e">/** 死锁的park/unpark */</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">parkUnparkDeadLockTest</span>() <span style="color:#66d9ef">throws</span> Exception {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 启动线程</span>
</span></span><span style="display:flex;"><span>		Thread consumerThread <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Thread(() <span style="color:#f92672">-&gt;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> (baozidian <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span>) { <span style="color:#75715e">// 如果没包子，则进入等待</span>
</span></span><span style="display:flex;"><span>				System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;1、进入等待&#34;</span>);
</span></span><span style="display:flex;"><span>				<span style="color:#75715e">// 当前线程拿到锁，然后挂起</span>
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">synchronized</span> (<span style="color:#66d9ef">this</span>) {
</span></span><span style="display:flex;"><span>					LockSupport.<span style="color:#a6e22e">park</span>();
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;2、买到包子，回家&#34;</span>);
</span></span><span style="display:flex;"><span>		});
</span></span><span style="display:flex;"><span>		consumerThread.<span style="color:#a6e22e">start</span>();
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 3秒之后，生产一个包子</span>
</span></span><span style="display:flex;"><span>		Thread.<span style="color:#a6e22e">sleep</span>(3000L);
</span></span><span style="display:flex;"><span>		baozidian <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Object();
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 争取到锁以后，再恢复consumerThread</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">synchronized</span> (<span style="color:#66d9ef">this</span>) {
</span></span><span style="display:flex;"><span>			LockSupport.<span style="color:#a6e22e">unpark</span>(consumerThread);
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;3、通知消费者&#34;</span>);
</span></span><span style="display:flex;"><span>	}
</span></span></code></pre></div><h4 id="伪唤醒">伪唤醒</h4>
<p><strong>警告!之前代码中用if语句来判断,是否进入等待状态,是错误的!</strong>
官方建议<strong>应该循环中检查等待条件</strong>，原因是处于等待状态的线程可能会收到<strong>错误警报和伪唤醒</strong>，如果不在循环中检查等待条件，程序就会在没有满足结束条件的情况下退出。</p>
<p>伪唤醒是指线程并非因为notify、notifyall、unpark等api调用而唤醒，是更底层原因导致的。</p>

        </article>
    </div>

    <div id="disqus_thread"></div>
<script>
    window.disqus_config = function () {
    
    
    
    };
    (function() {
        if (["localhost", "127.0.0.1"].indexOf(window.location.hostname) != -1) {
            document.getElementById('disqus_thread').innerHTML = 'Disqus comments not available by default when the website is previewed locally.';
            return;
        }
        var d = document, s = d.createElement('script'); s.async = true;
        s.src = '//' + "xie-yao-de-bo-ke" + '.disqus.com/embed.js';
        s.setAttribute('data-timestamp', +new Date());
        (d.head || d.body).appendChild(s);
    })();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
<a href="https://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>

            </div>
        </div><footer class="text-center pb-1">
    <small class="text-muted">
        
        &copy; Copyright 2024
        
        |
        <a href="https://beian.miit.gov.cn/" target="_blank">苏ICP备14012079号</a>
        <br />
        由 <a href="https://gohugo.io/" target="_blank">Hugo</a> 强力驱动
        | 主题 <a href="https://github.com/austingebauer/devise" target="_blank">Devise</a>
        <br />

    </small>
</footer></body>
</html>
